/* * Copyright 2014 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package de.codecentric.capturereplay.data; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.lang3.Validate; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.InitializingBean; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.lang.reflect.Method; public class JsonDataMapper implements DataMapper, InitializingBean { private ObjectMapper objectMapper; private CaptureFileProvider captureFileProvider; public JsonDataMapper(CaptureFileProvider captureFileProvider) { setCaptureFileProvider(captureFileProvider); } @Override public void writeCapturedData(MethodSignature signature, Object returnValue, Object[] arguments) throws DataMappingException { Method method = signature.getMethod(); String captureFileName = getCaptureFileName(method, arguments); try { File captureFile = captureFileProvider.getCaptureFile(captureFileName); enableTypeSupport(); objectMapper.writeValue(captureFile, new TypeWrapper(returnValue.getClass().getCanonicalName(), returnValue)); } catch (Exception e) { throw new DataMappingException(String.format("Could not write test data to file %s.", captureFileName), e); } } private void enableTypeSupport() { objectMapper.enableDefaultTypingAsProperty(ObjectMapper.DefaultTyping.NON_FINAL, "@class"); } @Override public Object getCapturedData(String methodName, Object[] arguments) throws DataMappingException { String captureFileName = generateCaptureFileName(methodName, getArgumentHashCodes(arguments)); try { enableTypeSupport(); TypeWrapper typeWrapper = objectMapper.readValue( new FileReader(captureFileProvider.getCaptureFile(captureFileName)), TypeWrapper.class ); return objectMapper.convertValue( typeWrapper.getObject(), objectMapper.getTypeFactory().constructFromCanonical(typeWrapper.getType()) ); } catch (IOException e) { throw new DataMappingException(String.format("Could not read from capture file %s.", captureFileName), e); } } protected String getCaptureFileName(Method method, Object[] arguments) { return generateCaptureFileName(method.getName(), getArgumentHashCodes(arguments)); } private long[] getArgumentHashCodes(Object[] arguments) { long[] argumentHashCodes = new long[arguments.length]; for (int i = 0; i < arguments.length; i++) { if (arguments[i] == null) { argumentHashCodes[i] = 0; } else if (Enum.class.isAssignableFrom(arguments[i].getClass())) { argumentHashCodes[i] = arguments[i].toString().hashCode(); } else { argumentHashCodes[i] = arguments[i].hashCode(); } } return argumentHashCodes; } private String generateCaptureFileName(String methodName, long[] argumentHashCodes) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append(methodName); for (long argumentHashCode : argumentHashCodes) { stringBuilder.append("-").append(argumentHashCode); } return stringBuilder.toString(); } @Override public void afterPropertiesSet() throws Exception { if (objectMapper == null) { objectMapper = createObjectMapper(); } Validate.notNull(captureFileProvider, "The capture file provider must be set."); } private ObjectMapper createObjectMapper() { ObjectMapper objectMapper = new ObjectMapper(); objectMapper.configure(MapperFeature.REQUIRE_SETTERS_FOR_GETTERS, true); objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true); objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false); objectMapper.configure(DeserializationFeature.READ_ENUMS_USING_TO_STRING, true); return objectMapper; } public void setObjectMapper(ObjectMapper objectMapper) { Validate.notNull(captureFileProvider, "The object mapper must not be null."); this.objectMapper = objectMapper; } public void setCaptureFileProvider(CaptureFileProvider captureFileProvider) { Validate.notNull(captureFileProvider, "The capture file provider must not be null."); this.captureFileProvider = captureFileProvider; } }